package cn.ucox.web.ms.plugin;

import cn.ucox.web.ms.components.cache.TableMetaInfoCache;
import cn.ucox.web.ms.persistence.IDataPersistence;
import cn.ucox.web.ms.persistence.SyncRowData;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Postgres数据库持久化组件
 *
 * @author chenw
 * @create 2017-07-28 23:34
 * @email javacspring@gmail.com
 */
public class PostgresDataPersistence implements IDataPersistence {

    private static final Logger logger = LoggerFactory.getLogger(PostgresDataPersistence.class);

    private static final int SYNC_DATA_STRUCTURE_ACTION_INSERT = 1;
    private static final int SYNC_DATA_STRUCTURE_ACTION_UPDATE = 2;
    private static final int SYNC_DATA_STRUCTURE_ACTION_DELETE = 4;

    private JdbcTemplate jdbcTemplate;

    public PostgresDataPersistence(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    @Override
    public void persistence(SyncRowData rowData) throws Exception {
        try {
            TableMetaInfoCache cache = TableMetaInfoCache.getInstance();

            //获取缓存中的meta数据以及表主键字段
            Map<String, String> metaData = cache.get(rowData.getTab());
            String pk = cache.getPk(rowData.getTab());

            if (null == metaData || metaData.isEmpty() || null == pk || "".equals(pk)) {
                logger.error("未配置表{}同步，无法加载元数据信息或者主键{}未设置", rowData.getTab(), pk);
                //将数据记录至同步失败通道
                throw new Exception("未配置表"+rowData.getTab()+"同步，无法加载元数据信息或者主键"+pk+"未设置");
            }
            List<String> columns = new ArrayList<>();
            List<String> qm = new ArrayList<>();
            List<Object> values = new ArrayList<>();
            List<Object> upValues = new ArrayList<>();
            List<Integer> upTypes = new ArrayList<>();
            List<Integer> types = new ArrayList<>();
            List<String> updates = new ArrayList<>();
            Object pkv = "";
            int pkt = 0;

            JSONObject data = JSONObject.parseObject(rowData.getData());
            for (Map.Entry<String, Object> entry : data.entrySet()) {
                String key = entry.getKey(), keyType = metaData.get(key);
                if ("__att_content__".equalsIgnoreCase(key) || "__att_col_name__".equalsIgnoreCase(key)) {
                    continue;
                }
                int javaType = Integer.parseInt(keyType);
                columns.add(key);
                qm.add("?");
                types.add(javaType);
                values.add(this.setValue(entry.getValue(), javaType));

                if (key.equalsIgnoreCase(pk)) {
                    pkv = entry.getValue();
                    pkt = javaType;
                } else {
                    updates.add(key + " = ?");
                    upValues.add(this.setValue(entry.getValue(), javaType));
                    upTypes.add(javaType);
                }
            }
            upValues.add(pkv);
            upTypes.add(pkt);


            String sql;

            //insert
            if (SYNC_DATA_STRUCTURE_ACTION_INSERT == rowData.getAct()) {
                sql = String.format("INSERT INTO %s(%s) VALUES(%s)", rowData.getTab(), StringUtils.join(columns.toArray(), ","), StringUtils.join(qm.toArray(), ","));
                //数据库持久化
                //types 从 List<Integer> 转换成 int[] 形式
                this.update(sql, values.toArray(), types.stream().mapToInt(Integer::valueOf).toArray());
            }

            //update
            if (SYNC_DATA_STRUCTURE_ACTION_UPDATE == rowData.getAct()) {
                sql = String.format("UPDATE %s SET %s WHERE %s = ?", rowData.getTab(), StringUtils.join(updates.toArray(), ","),pk);
                //数据库持久化
                //types 从 List<Integer> 转换成 int[] 形式
                this.update(sql, upValues.toArray(), upTypes.stream().mapToInt(Integer::valueOf).toArray());
            }

            //delete
            if (SYNC_DATA_STRUCTURE_ACTION_DELETE == rowData.getAct()) {
                sql = String.format("DELETE FROM %s WHERE %s = ?", rowData.getTab(), pk);
                //数据库持久化
                //types 从 List<Integer> 转换成 int[] 形式
                this.update(sql, new Object[]{pkv}, new int[]{pkt});
            }


        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    private void update(String sql, Object[] values, int[] types) throws Exception {
        logger.debug("execute: {}", sql);
        //TODO:记录日志
        try {
            jdbcTemplate.update(sql, values, types);
        } catch (DataAccessException e) {
            logger.error("插入表{}记录:{}:失败 ", sql, values,e);
            throw e;
        }
    }

    //格式转换
    public Object setValue(Object obj, int keyType) throws Exception {
        if (obj == null) {
            return null;
        }
        if (Types.TIMESTAMP == keyType) {
            if (obj.equals("")) {
                return null;
            } else {
                return Timestamp.valueOf(obj.toString().replace("T", " "));
            }
        } else if (Types.DATE == keyType) {
            if (obj.equals("")) {
                return null;
            }
            return Date.valueOf(obj.toString());
        } else if (Types.BLOB == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return obj;
//            return new SerialBlob(obj.toString().getBytes("GBK"));
        } else if (Types.CLOB == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return obj;
//            return new SerialClob(obj.toString().toCharArray());
        } else if (Types.NCLOB == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return obj;
        } else if (Types.BOOLEAN == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Boolean.valueOf(obj.toString());
        } else if (Types.DECIMAL == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return new BigDecimal(obj.toString());
        } else if (Types.DOUBLE == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Double.valueOf(obj.toString());
        } else if (Types.FLOAT == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Float.valueOf(obj.toString());
        } else if (Types.INTEGER == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Integer.valueOf(obj.toString());
        } else if (Types.BIT == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Boolean.valueOf(obj.toString());
        } else if (Types.NUMERIC == keyType) {
            if (obj.equals("")) {
                return null;
            }
            return new BigDecimal(obj.toString());
        } else if (Types.TIMESTAMP_WITH_TIMEZONE == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Timestamp.valueOf(obj.toString().replace("T", " "));
        } else if (Types.TINYINT == keyType) {
            if (obj.equals("")) {
                return obj;
            }
            return Integer.valueOf(obj.toString());
        }
        return obj;

    }


}
